package top.gokzzz.common.utils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.ToIntFunction;
import java.util.stream.Collectors;

/**
 * Stream 流 工具類
 *
 * @author Gok
 * @version 1.2
 */
public class StreamUtil {

    /**
     * 過濾
     *
     * @param collection 集合
     * @param function   过滤方法
     * @return {@link List }<{@link E }>
     */
    public static <E> List<E> filterBy(Collection<E> collection, Predicate<E> function) {
        if (CollUtil.isEmpty(collection)) {
            return new ArrayList<>();
        }
        return collection.stream()
                .filter(function)
                .collect(Collectors.toList());
    }

    /**
     * 拿出其中一個屬性收集成集合
     *
     * @param collection 集合
     * @param function   屬性方法
     * @return {@link List }<{@link T }>
     */
    public static <E, T> List<T> listBy(Collection<E> collection, Function<E, T> function) {
        if (CollUtil.isEmpty(collection)) {
            return new ArrayList<>();
        }
        return collection.stream()
                .map(function)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    /**
     * 根據對象中一個屬性進行分組
     *
     * @param collection 集合
     * @param function   屬性方法
     * @return {@link Map }<{@link K }, {@link List }<{@link E }>>
     */
    public static <E, K> Map<K, List<E>> groupBy(Collection<E> collection, Function<E, K> function) {
        if (CollUtil.isEmpty(collection)) {
            return new HashMap<>();
        }
        return collection.stream()
                .filter(Objects::nonNull)
                .collect(Collectors.groupingBy(function, LinkedHashMap::new, Collectors.toList()));
    }

    /**
     * 根據對象中一個屬性進行去重
     *
     * @param collection 集合
     * @param function   屬性方法
     * @return {@link List }<{@link E }>
     */
    public static <E, T> List<E> distinctBy(Collection<E> collection, Function<E, T> function) {
        if (CollUtil.isEmpty(collection) || function == null) {
            return new ArrayList<>();
        }
        return collection.stream()
                .filter(Objects::nonNull)
                .collect(Collectors.collectingAndThen(
                        Collectors.toMap(function, e -> e, (e1, e2) -> e1),
                        map -> new ArrayList<>(map.values())
                ));
    }

    /**
     * 根據對象中一個屬性進行排序
     *
     * @param collection 集合
     * @param function   屬性方法
     * @param comparator 排序規則
     * @return {@link List }<{@link E }>
     */
    public static <E, T> List<E> sortedBy(Collection<E> collection, Function<E, T> function, Comparator<T> comparator) {
        if (CollUtil.isEmpty(collection) || function == null || comparator == null) {
            return new ArrayList<>();
        }
        return collection.stream()
                .filter(Objects::nonNull)
                .sorted(Comparator.comparing(function, comparator))
                .collect(Collectors.toList());
    }

    /**
     * 對集合進行分頁
     *
     * @param collection 集合
     * @param pageNum    頁碼（ 1 開始）
     * @param pageSize   每頁大小
     * @return {@link List }<{@link E }>
     */
    public static <E> List<E> page(Collection<E> collection, int pageNum, int pageSize) {
        if (CollUtil.isEmpty(collection) || pageNum < 1 || pageSize < 1) {
            return new ArrayList<>();
        }
        return collection.stream()
                .skip((long) (pageNum - 1) * pageSize)
                .limit(pageSize)
                .collect(Collectors.toList());
    }

    /**
     * 對集合中某個屬性進行求和
     *
     * @param collection 集合
     * @param function   屬性方法（將對象轉換為 int）
     * @return 總和
     */
    public static <E> int sumBy(Collection<E> collection, ToIntFunction<E> function) {
        if (CollUtil.isEmpty(collection) || function == null) {
            return 0;
        }
        return collection.stream()
                .filter(Objects::nonNull)
                .mapToInt(function)
                .sum();
    }

}
